﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using Krile.Kernel;
using Krile.Kernel.WindowsEx;
using Twinkle.Common;
using Twinkle.Tweak;
using System.Linq;
using System.Text.RegularExpressions;
using System.IO;
using System.Net;

namespace Twinkle.Data
{
    public class Account
    {
        public int RateLimitRemain
        {
            get
            {
                if (oAuth != null)
                    return oAuth.RateLimitRemaining;
                else
                    return 0;
            }
        }

        public int RateLimitMax
        {
            get
            {
                if (oAuth != null)
                    return oAuth.RateLimitMax;
                else
                    return 0;
            }
        }

        #region Use for extended api consumption determination

        public int ApiRealConsumption = 0;

        #endregion

        public TList[] FollowingLists { get; set; }

        public string[] ReceiveReservedLists { get; set; }

        public DateTime RateLimitReset
        {
            get
            {
                if (oAuth != null)
                    return oAuth.RateLimitReset;
                else
                    return DateTime.Now;
            }
        }

        public CredentialProvider CredentialProvider
        {
            get { return oAuth; }
        }

        #region Privacy datas

        public string UserId = String.Empty;

        [XmlIgnore()]
        public string Token = String.Empty;

        [XmlIgnore()]
        public string Secret = String.Empty;

        public string UserToken
        {
            get { return Bridge.CommonOperation.AESEncrypt(Token); }
            set
            {
                try
                {
                    this.Token = Bridge.CommonOperation.AESDecrypt(value);
                }
                catch (Exception e)
                {
                    Bridge.DebugReporter.AddReport(e);
                    this.Token = String.Empty;
                }
            }
        }

        public string UserSecret
        {
            get { return Bridge.CommonOperation.AESEncrypt(Secret); }
            set
            {
                try
                {
                    this.Secret = Bridge.CommonOperation.AESDecrypt(value);
                }
                catch (Exception e)
                {
                    Bridge.DebugReporter.AddReport(e);
                    this.Secret = String.Empty;
                }
            }
        }

        #endregion

        #region Using states

        [Flags()]
        public enum UsingStates { None = 0, Receive = 1, Update = 2, Operate = 4 }

        public UsingStates UsingState = UsingStates.Update | UsingStates.Receive | UsingStates.Operate;

        public bool UseForUpdate
        {
            get { return (UsingState & UsingStates.Update) == UsingStates.Update; }
            set
            {
                if (value)
                    UsingState |= UsingStates.Update;
                else
                    UsingState &= ~UsingStates.Update;
            }
        }

        public bool UseForReceive
        {
            get { return (UsingState & UsingStates.Receive) == UsingStates.Receive; }
            set
            {
                if (value)
                    UsingState |= UsingStates.Receive;
                else
                    UsingState &= ~UsingStates.Receive;
            }
        }

        public bool UseForOperate
        {
            get { return (UsingState & UsingStates.Operate) == UsingStates.Operate; }
            set
            {
                if (value)
                    UsingState |= UsingStates.Operate;
                else
                    UsingState &= ~UsingStates.Operate;
            }
        }

        #endregion

        #region Receiving parameters

        public bool UseUserStreams = false;

        public bool UpdatesDinamically = true;

        #region Dymanic update
        public int ApiUseLimit = 70;

        public int HomeTimelineReceivePercent = 100;

        public int ReplyFeedReceivePercent = 20;

        public int DMReceivePercent = 5;

        public int ListReceivePercent = 50;

        public bool UseExtendedAPIComsuptionDetermination = false;
        #endregion

        #region Static update
        public int HomeTimelineReceiveInterval = 30;

        public int ReplyFeedReceiveInterval = 120;

        public int DMReceiveInterval = 360;

        public int ListReceiveInterval = 200;
        #endregion

        #endregion

        #region Suffix signature
        public bool UseSignature = false;

        public string Signature = String.Empty;
        #endregion

        #region Twitter properties

        public string[] followingUsers { get; set; }

        #endregion

        private Tweak.CredentialProviders.OAuth oAuth;

        public Account()
        {
            oAuth = new Twinkle.Tweak.CredentialProviders.OAuth(this);
        }

        #region Operations
        //RT extraction regex
        private static Regex RTExtractRegex = new Regex(@".*RT (@[a-zA-Z0-9]+)(?::| ) ?(.*)");

        public static IEnumerable<TStatus> OuterInjectionSetFnFilterNRecv(IEnumerable<TStatus> statuses)
        {
            if (statuses == null)
                yield break;
            foreach (var s in statuses)
            {
                if (s == null || String.IsNullOrEmpty(s.Id) || s.User == null || String.IsNullOrEmpty(s.User.Id)) // invalid data
                    continue;
                if (Kernel.Storage.GetUid(s.Id) != 0) //duplicated
                    continue;
                string replyingUserId = null;
                if (s.ReplyData != null)
                {
                    replyingUserId = s.ReplyData.ReplyToUserId;
                }
                else if (Kernel.Config.Behavior.AutoLookupRTOrigin)
                {
                    //detect origin of rt/qt
                    //QT -> RT
                    string formatted = s.Text.Original.Replace("QT", "RT");
                    if (formatted.Contains("RT"))
                    {
                        var m = RTExtractRegex.Match(formatted);
                        if (m.Success)
                        {
                            try
                            {
                                string tweetId = null;
                                System.Diagnostics.Debug.WriteLine("RT found:" + formatted + " by " + s.User.Id);
                                string firstTweetId = null;
                                var originUserId = m.Groups[1].Value.TrimStart('@');
                                var originStatusText = m.Groups[2].Value;
                                System.Diagnostics.Debug.WriteLine(" USER ID:[" + originUserId + "] TEXT:[" + originStatusText + "]");
                                foreach (var uid in Kernel.Storage.GetRegisteredUids())
                                {
                                    var t = Bridge.StatusManager.GetStatus(uid) as TStatus;
                                    if (t == null) continue;
                                    if (t.Text.Original.Contains(originStatusText))
                                    {
                                        //だいたいあってる
                                        tweetId = t.Id;
                                        if (t.User != null && t.User.Id == originUserId)
                                            //まぁこれだろう
                                            break;
                                    }
                                    else if (t.User != null && t.User.Id == originUserId && firstTweetId == null)
                                    {
                                        //ユーザーIDはあってる
                                        firstTweetId = t.User.Id;
                                    }
                                }
                                System.Diagnostics.Debug.WriteLine("tweet id is " + tweetId);
                                if (tweetId != null)
                                {
                                    s.ReplyData = new TReplyData(tweetId, originUserId);
                                }
                                else
                                {
                                    s.ReplyData = new TReplyData(null, originUserId);
                                }
                                replyingUserId = originUserId;
                            }
                            catch { }
                        }
                    }
                }
                s.Text = s.Text.Original.CreateXString(replyingUserId, s.StatusKind);
                var cstatus = s;
                if (!Kernel.Plugin.TOperation.IsMasterId(s.User.Id))
                {
                    List<Krile.Kernel.Control.Surface.CommandButton> commands = new List<Krile.Kernel.Control.Surface.CommandButton>();
                    if (!s.User.Protected)
                    {
                        if (Kernel.Config.StatusDesign.ShowRTButton)
                        {
                            var rt = new Krile.Kernel.Control.Surface.CommandButtonBasicImpl(Kernel.ImagesCache.RTImage, Lang.General.Status_RTThis);
                            rt.OnMouseClicked += new Action<Krile.Kernel.Control.Surface.ChainMouseEventArgs>((e) =>
                            {
                                if (MessageBoxEx.Show(Lang.Msg.RetweetConfirm, Lang.Msg.RetweetConfirm_Title, MessageBoxEx.MessageBoxExButtons.YesNo, MessageBoxEx.MessageBoxExIcon.Question) == MessageBoxEx.DialogResultEx.Yes)
                                {
                                    Krile.Kernel.Bridge.MainForm.SetStatusText("Retweeting...");
                                    try
                                    {
                                        TwitterCommonOperation.Retweet(cstatus.UID);
                                        Krile.Kernel.Bridge.MainForm.SetStatusText("Retweeted.", 3);
                                    }
                                    catch (Exception ex)
                                    {
                                        Krile.Kernel.Bridge.MainForm.SetStatusText("Retweet error:" + ex.Message, 10);
                                    }
                                }
                            });
                            commands.Add(rt);
                        }
                        if (Kernel.Config.StatusDesign.ShowUnofficialRTButton)
                        {
                            var rtp = new Krile.Kernel.Control.Surface.CommandButtonBasicImpl(Kernel.ImagesCache.URTImage, Lang.General.Status_UORTThis);
                            rtp.OnMouseClicked += new Action<Krile.Kernel.Control.Surface.ChainMouseEventArgs>((e) =>
                            {
                                TwitterCommonOperation.UnofficialRetweet(cstatus.User.Id, cstatus.Text.Original);
                            });
                            commands.Add(rtp);
                        }
                    }
                    if (Kernel.Config.StatusDesign.ShowQTButton)
                    {
                        var qt = new Krile.Kernel.Control.Surface.CommandButtonBasicImpl(Kernel.ImagesCache.QTImage, Lang.General.Status_QTThis);
                        qt.OnMouseClicked += new Action<Krile.Kernel.Control.Surface.ChainMouseEventArgs>((e) =>
                        {
                            TwitterCommonOperation.Quote(cstatus.Id, cstatus.User.Id, cstatus.Text.Original);
                        });
                        commands.Add(qt);
                    }
                    if (commands.Count > 0)
                        s.Commands = commands.ToArray();
                }
                else
                {
                    System.Diagnostics.Debug.WriteLine("My post:" + s.User.Id + " - " + s.Text.Original);
                }
                if (Kernel.Storage.GetUid(s.Id) != 0) //duplicated
                    continue;
                Kernel.Storage.AddSidUid(s.Id, s.UID);
                yield return s;
            }
            yield break;
        }

        private IEnumerable<TStatus> SetFormatAndFilteringNewReceives(IEnumerable<TStatus> statuses)
        {
            return OuterInjectionSetFnFilterNRecv(statuses);
        }

        public IEnumerable<TStatus> ReceiveTimeline()
        {
            try
            {
                var recv = oAuth.GetHomeTimeline(null, null, Kernel.Config.StatusCountPerRecv, null);
                if (recv == null) return null;
                var recvs = from s in recv
                            orderby s.CreatedAt
                            select s;
                return SetFormatAndFilteringNewReceives(recvs);
            }
            catch (IOException) { return null; }
            catch (WebException) { return null; }
        }

        public IEnumerable<TStatus> ReceivePublicTimeline()
        {
            try
            {
                var recv = oAuth.GetPublicTimeline();
                if (recv == null) return null;
                var recvs = from s in recv
                            orderby s.CreatedAt
                            select s;
                return SetFormatAndFilteringNewReceives(recvs);
            }
            catch (IOException) { return null; }
            catch (WebException) { return null; }
        }

        public IEnumerable<TStatus> ReceiveMentions()
        {
            try
            {
                var recv = oAuth.GetMentions(null, null, Kernel.Config.StatusCountPerRecv, null);
                if (recv == null) return null;
                var recvs = from s in recv
                            orderby s.CreatedAt
                            select s;
                return SetFormatAndFilteringNewReceives(recvs);
            }
            catch (IOException) { return null; }
            catch (WebException) { return null; }
        }

        public IEnumerable<TStatus> ReceiveDMs()
        {
            try
            {
                var dm = oAuth.GetDirectMessages(null, null, Kernel.Config.StatusCountPerRecv, null);
                if (dm == null) return null;
                var recvs = from s in dm
                            orderby s.CreatedAt
                            select s;
                return SetFormatAndFilteringNewReceives(recvs);
            }
            catch (IOException) { return null; }
            catch (WebException) { return null; }
        }

        public IEnumerable<TStatus> ReceiveListTimeline(string user, string id)
        {
            try
            {
                var recv = oAuth.GetListStatuses(user, id);
                if (recv == null) return null;
                var recvs = from s in recv
                            orderby s.CreatedAt
                            select s;
                return SetFormatAndFilteringNewReceives(recvs);
            }
            catch (IOException) { return null; }
            catch (WebException) { return null; }
        }

        public OperationResult<object> Update(string text, string in_reply_to)
        {
            try
            {
                long irt = -1;
                TStatus upd = null;
                if (!String.IsNullOrEmpty(in_reply_to) && long.TryParse(in_reply_to, out irt))
                    upd = this.oAuth.UpdateStatus(text, irt);
                else
                    upd = this.oAuth.UpdateStatus(text, null);
                if (upd == null)
                    throw new Exception("unknown error in updating status on twitter");
                var upda = new[] { upd };
                Common.AutoReceiver.RegistStatuses(SetFormatAndFilteringNewReceives(upda));
                return new OperationResult<object>() { Succeeded = true };
            }
            catch (Exception e)
            {
                return new OperationResult<object>() { Message = e.Message, Succeeded = false, Exception = e };
            }
        }

        public OperationResult<object> SendDirectMessage(string text, string recipientId)
        {
            try
            {
                TStatus upd = this.oAuth.SendDirectMessage(recipientId, text);
                if (upd == null)
                    throw new Exception("unknown error in sending direct message on twitter");
                var upda = new[] { upd };
                Common.AutoReceiver.RegistStatuses(SetFormatAndFilteringNewReceives(upda));
                return new OperationResult<object>() { Succeeded = true };
            }
            catch (Exception e)
            {
                return new OperationResult<object>() { Message = e.Message, Succeeded = false, Exception = e };
            }
        }

        public OperationResult<object> SetFavorite(string status_id, bool create)
        {
            try
            {
                long tid;
                if (!long.TryParse(status_id, out tid))
                    throw new ArgumentException("status id is invalid:" + status_id);
                TStatus fav = null;
                if (create)
                    fav = oAuth.CreateFavorites(tid);
                else
                    fav = oAuth.DestroyFavorites(tid);
                if (fav == null)
                    throw new Exception("unknown error has occured in creating favorite on twitter");
                return new OperationResult<object>() { Succeeded = true };
            }
            catch (Exception e)
            {
                return new OperationResult<object>() { Message = e.Message, Succeeded = false, Exception = e };
            }
        }

        internal OperationResult<object> Retweet(string sid)
        {
            try
            {
                long tid;
                if (!long.TryParse(sid, out tid))
                    throw new ArgumentException("invalid status id:" + sid);
                var rt = oAuth.Retweet(tid);
                if (rt == null)
                    throw new Exception("unknown error has occured in retweeting tweets on twitter");
                return new OperationResult<object>() { Succeeded = true };
            }
            catch (Exception e)
            {
                return new OperationResult<object>() { Message = e.Message, Succeeded = false, Exception = e };
            }
        }

        #endregion

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(this.UserId);
            sb.Append(" ");
            if ((UsingState & UsingStates.Receive) == UsingStates.Receive)
                sb.Append("[Recv]");
            if ((UsingState & UsingStates.Update) == UsingStates.Update)
                sb.Append("[Post]");
            if ((UsingState & UsingStates.Operate) == UsingStates.Operate)
                sb.Append("[Oper]");
            return sb.ToString();
        }
    }
}